﻿using HouseKeeperCore.Elemental;
using System.Collections.Generic;
using HouseKeeperCore.Hierarchical;

namespace BillSplitter
{
    /// <summary>
    ///  Provides a straighforward linear solution to splitting the bills of service usage
    /// </summary>
    public class LinearSolver
    {
        #region Fields

        /// <summary>
        ///  All entities that are registered and can be used in the calculation
        /// </summary>
        private readonly IDictionary<long, Entity> _allEntities = new Dictionary<long, Entity>();

        /// <summary>
        ///  All entities that are currently active in the calculation
        /// </summary>
        private readonly IList<Entity> _activeEntities = new List<Entity>();

        /// <summary>
        ///  A sequence of events to process
        /// </summary>
        private readonly IList<Event> _events = new List<Event>();

        /// <summary>
        ///  All charge instances that is active at the current time
        /// </summary>
        private readonly IList<EvenCharge> _charges = new List<EvenCharge>();

        #endregion

        #region Methods

        /// <summary>
        ///  Processes the events and solves the case
        /// </summary>
        void Process()
        {
            foreach (var e in _events)
            {
                if (e is StructureEvent)
                {
                    HandleStructureEvent((StructureEvent)e);
                }
                else if (e is ChargeEvent)
                {
                    HandleChargeEvent((ChargeEvent)e);
                }
            }
        }

        /// <summary>
        ///  Processes a charge event
        /// </summary>
        /// <param name="e">The charge event</param>
        private void HandleChargeEvent(ChargeEvent e)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        ///  Processes a structure event
        /// </summary>
        /// <param name="e">The structure event</param>
        private void HandleStructureEvent(StructureEvent e)
        {
            switch (e.Action)
            {
                case StructureEvent.ChangeType.AddEntity:
                    AddEntity(e.Entity);
                    break;
                case StructureEvent.ChangeType.RemoveEntity:
                    RemoveEntity(e.Entity);
                    break;
                case StructureEvent.ChangeType.AddToGroup:
                    AddToGroup(e.Entity, e.Group);
                    break;
                case StructureEvent.ChangeType.RemoveFromGroup:
                    RemoveFromGroup(e.Entity, e.Group);
                    break;
            }
            throw new System.NotImplementedException();
        }

        /// <summary>
        ///  Adds an entity to the solver as per the corresponding event
        /// </summary>
        /// <param name="entity">The entity to add to the solver</param>
        private void AddEntity(TaggedEntity entity)
        {
            if (_allEntities.ContainsKey(entity.Id))
            {
                throw new HouseKeeperException("Entity to add already exists in the solver");
            }

            _allEntities.Add(entity.Id, entity); 
        }

        /// <summary>
        ///  Removes the entity with the same Id as the specified entity (so they may not be exactly the same) 
        ///  from the solver as per the corresponding event
        /// </summary>
        /// <param name="entity">The entity to remove from the solver</param>
        private void RemoveEntity(TaggedEntity entity)
        {
            if (!_allEntities.ContainsKey(entity.Id))
            {
                throw new HouseKeeperException("Entity to remove doesn't exist in the solver");
            }

            _allEntities.Remove(entity.Id);
        }

        /// <summary>
        ///  Adds the entity to the group
        /// </summary>
        /// <param name="entity">The entity to add to the group</param>
        /// <param name="group">The group the entity is to be add to</param>
        private void AddToGroup(TaggedEntity entity, BillGroup group)
        {
            if (group.TaggedItemsLookup.ContainsKey(entity.Id))
            {
                throw new HouseKeeperException("Entity to add already exists in the group");
            }

            group.TaggedItemsLookup.Add(entity.Id, entity);
        }

        /// <summary>
        ///  Removes an entity with the same ID as the specified entity from the group
        /// </summary>
        /// <param name="entity">The entity to remove from the group</param>
        /// <param name="group">The group the entity is to be removed from</param>
        private void RemoveFromGroup(TaggedEntity entity, BillGroup group)
        {
            if (!group.TaggedItemsLookup.ContainsKey(entity.Id))
            {
                throw new HouseKeeperException("Enttiy to remove doesn't exist in the group");
            }

            group.TaggedItemsLookup.Remove(entity.Id);
        }

        #endregion
    }
}
